/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
 * Description: demuxer common function
 * Author: HiMobileCam middleware develop team
 * Create: 2017-12-22
 */

#include "common_format.h"

#ifndef __HuaweiLite__
#include <sys/syscall.h>
#endif

#include "securec.h"

#include "hi_demuxer_err.h"
#include "ffdemux_common.h"
#include "ffdemux_log.h"

#include "ts_format.h"
#include "mp4_format.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */

#define TS_DEFAULT_FPS 30
/* define HI_NULL handle for codedex check */
#define FFMPEG_TIME_UNIT   1000
#define FFMPEG_DEFAULT_FPS 30

#define TIME_MS2US 1000
#define TIME_SS2US 1000000
#define TIME_M2SS  60
#define TIME_H2M   60

static HI_S32 DemuxerSetAudioInfo(FfDemuxFormatMember *formatMember, HI_FORMAT_PACKET_S *fmtFrame)
{
    fmtFrame->pu8Data = formatMember->pkt.data;
    fmtFrame->u32Size = formatMember->pkt.size;
    formatMember->hasAdts = HI_FALSE;
    formatMember->lastReadPts = av_rescale_q(formatMember->pkt.pts,
        formatMember->audioStream->time_base, AV_TIME_BASE_Q);
    return HI_SUCCESS;
}

static HI_S32 DemuxerGetFrame(FfDemuxFormatMember *demuxer)
{
    /* read frames from the file */
    HI_S32 ret = av_read_frame(demuxer->formatContext, &demuxer->pkt);
    if (ret == AVERROR_EOF) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "read endof ret: %d\n", ret);
        return HI_RET_FILE_EOF;
    } else if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "av_read_frame failed, ret:%d\n", ret);
        return HI_ERR_DEMUXER_READ_PACKET;
    }

    if (demuxer->pkt.size < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Demux err:stream len:%d\n", demuxer->pkt.size);
        return HI_ERR_DEMUXER_READ_PACKET;
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerGetFileSize(const FfDemuxFormatMember *formatMember, HI_S64 *outFileSize)
{
    FILE *mediaFd = fopen(formatMember->filePath, "rb");
    if (mediaFd == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, " formatMember->filePath:%s , err info:%s\n",
            formatMember->filePath, strerror(errno));
        return HI_FAILURE;
    }

    HI_S32 ret = fseeko(mediaFd, 0, SEEK_END);
    /* if error comes, fseek return  -1 */
    if (ret == -1) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, " fseeko exec failed,  err info:%s\n",
            strerror(errno));
        fclose(mediaFd);
        return HI_FAILURE;
    }

    HI_S64 fileSize = (HI_S64)(ftello(mediaFd));
    /* if error comes, ftell return  -1 */
    if (fileSize == -1) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, " ftello exec failed,  err info:%s\n",
            strerror(errno));
        fclose(mediaFd);
        return HI_FAILURE;
    }

    *outFileSize = fileSize;
    fclose(mediaFd);
    return HI_SUCCESS;
}

static HI_S32 DemuxerMp4ProbeVideio(FfDemuxFormatMember *demuxer, HI_U32 streamNum, HI_BOOL *outBsInite)
{
    HI_S32 ret = HI_SUCCESS;
    HI_BOOL bsfInited = HI_FALSE;
    if (AV_CODEC_ID_H264 == demuxer->vidCodecId
        || demuxer->vidCodecId == AV_CODEC_ID_HEVC) {
        const HI_CHAR *filterName = (demuxer->vidCodecId == AV_CODEC_ID_H264) ?
            "h264_mp4toannexb" : "hevc_mp4toannexb";
        ret = Mp4AvbsfInit(&demuxer->h26XBsfCtx, demuxer->videoStream, filterName);
        if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "Mp4AvbsfInit h264_mp4toannexb failed\n");
            return HI_ERR_DEMUXER_PROBE;
        }
        bsfInited = HI_TRUE;
    }
    streamNum = (streamNum > HI_DEMUXER_RESOLUTION_CNT) ? (HI_DEMUXER_RESOLUTION_CNT) : (streamNum);
    for (HI_U32 i = 0; i < streamNum; i++) {
        if (demuxer->formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            demuxer->stSteamResolution[i].s32VideoStreamIndex = i;
            demuxer->stSteamResolution[i].u32Height = demuxer->formatContext->streams[i]->codecpar->height;
            demuxer->stSteamResolution[i].u32Width = demuxer->formatContext->streams[i]->codecpar->width;
            /* this demux lib will only deal with AV_CODEC_ID_H264/HI_FFMPEG_VIDEO_TYPE_H265 */
            if (demuxer->formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264) {
                demuxer->stSteamResolution[i].enVideoType = HI_FORMAT_VIDEO_TYPE_H264;
            } else if (demuxer->formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_HEVC) {
                demuxer->stSteamResolution[i].enVideoType = HI_FORMAT_VIDEO_TYPE_H265;
            } else {
                demuxer->stSteamResolution[i].enVideoType = HI_FORMAT_VIDEO_TYPE_BUTT;
            }
        }
    }
    *outBsInite = bsfInited;
    return ret;
}

static HI_S32 DemuxerProbeVideo(FfDemuxFormatMember *demuxer, HI_BOOL *outBsInited)
{
    if ((demuxer->enFormat != HI_FFDEMUX_FORMAT_TS) && (demuxer->enFormat != HI_FFDEMUX_FORMAT_MP4)) {
        demuxer->videoStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        demuxer->videoStream = HI_NULL;
        return HI_SUCCESS;
    }

    HI_S32 ret = av_find_best_stream(demuxer->formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, HI_NULL, 0);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
            "Could not find video stream in input file '%s', ret:%d \n", demuxer->filePath, ret);
        demuxer->videoStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        demuxer->videoStream = HI_NULL;
        return HI_SUCCESS;
    }
    demuxer->videoStream = demuxer->formatContext->streams[ret];
    demuxer->videoStreamIndex = ret;
    demuxer->usedVideoStreamIndex = demuxer->videoStreamIndex;
    demuxer->usedVideoStream = demuxer->videoStream;
    demuxer->vidCodecId = demuxer->videoStream->codecpar->codec_id;
    demuxer->enUsedVidCodecId = demuxer->vidCodecId;
    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "video codec type  %s\n",
        avcodec_get_name(demuxer->vidCodecId));
    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "video timebase: %d %d \n",
        demuxer->videoStream->time_base.den, demuxer->videoStream->time_base.num);

    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        HI_U32 streamNum = demuxer->formatContext->nb_streams;
        ret = DemuxerMp4ProbeVideio(demuxer, streamNum, outBsInited);
    } else if (demuxer->enFormat == HI_FFDEMUX_FORMAT_TS) {
        // get video stream num
        for (HI_U32 i = 0; i < demuxer->formatContext->nb_streams; i++) {
            if ((demuxer->formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264)
                || (demuxer->formatContext->streams[i]->codecpar->codec_id == AV_CODEC_ID_HEVC)) {
                demuxer->videoStreamNum++;
            }
        }
        ret = HI_SUCCESS;
    }

    return ret;
}

static HI_S32 DemuxerProbeAudio(FfDemuxFormatMember *formatMember)
{
    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_TS) {
        HI_S32 ret = TsProbeAudio(formatMember);
        if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "TsProbeAudio failed\n");
            ret = HI_ERR_DEMUXER_PROBE;
        }
        return ret;
    }

    HI_S32 ret = av_find_best_stream(formatMember->formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, HI_NULL, 0);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
            "Could not find audio stream in input file '%s', ret:%d\n",
            formatMember->filePath, ret);
        formatMember->audioStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        formatMember->audioStream = HI_NULL;
        return HI_SUCCESS;
    }

    formatMember->audioStream = formatMember->formatContext->streams[ret];
    formatMember->audioStreamIndex = ret;
    formatMember->enAudCodecId = formatMember->audioStream->codecpar->codec_id;
    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
        "audio codec type  %s\n", avcodec_get_name(formatMember->enAudCodecId));
    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "audio timebase: %d %d \n",
        formatMember->audioStream->time_base.den, formatMember->audioStream->time_base.num);
    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_WAV) {
        if (formatMember->enAudCodecId != AV_CODEC_ID_PCM_S16LE) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "WAV_Probe failed, not support wav codec %s\n", avcodec_get_name(formatMember->enAudCodecId));
            return HI_ERR_DEMUXER_PROBE;
        }
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerProbeMeta(FfDemuxFormatMember *formatMember)
{
    HI_S32 ret;

    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_TS) {
        ret = TsProbeByParseFrame(formatMember);
        if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Could not probe video&audio media info\n");
            return HI_ERR_DEMUXER_PROBE;
        }
    }else if (formatMember->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        ret = Mp4ProbeMeta(formatMember);
        if (ret != HI_SUCCESS) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Mp4ProbeMeta failed\n");
            return HI_ERR_DEMUXER_PROBE;
        }
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerSeekfile(FfDemuxFormatMember *formatMember, HI_S32 streamIndex, HI_S64 toMs, HI_S32 flag)
{
    HI_S64 seekTarget = ((HI_S64)toMs * TIME_MS2US);
    /* timeUs may oversize s64 to be negative value */
    if (toMs < 0 || seekTarget < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "seek input time is negative or oversize: %lld ms\n", toMs);
        return HI_ERR_DEMUXER_ILLEGAL_PARAM;
    }

    if (seekTarget > formatMember->formatContext->duration) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
            "seek input time beyond total time seektime: %lld\n", seekTarget);
        seekTarget = formatMember->formatContext->duration;
    }

    if (streamIndex == formatMember->usedVideoStreamIndex && formatMember->usedVideoStream) {
        HI_S64 videoDuration = formatMember->usedVideoStream->duration * FFMPEG_TIME_UNIT *
            formatMember->usedVideoStream->time_base.num / formatMember->usedVideoStream->time_base.den;
        if (toMs > videoDuration) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
                "seek input time beyond total video time seektime: %lld videoDuration %lld \n", toMs, videoDuration);
            seekTarget = (HI_S64)videoDuration * TIME_MS2US;
        }
    }

    if (formatMember->formatContext->start_time != AV_NOPTS_VALUE) {
        seekTarget += formatMember->formatContext->start_time;
    }

    if (streamIndex != -1 && streamIndex < (HI_S32)formatMember->formatContext->nb_streams) {
        seekTarget = av_rescale(seekTarget,
            formatMember->formatContext->streams[streamIndex]->time_base.den,
            AV_TIME_BASE * (HI_S64)formatMember->formatContext->streams[streamIndex]->time_base.num);
    }

    HI_S64 calcSeekMin = seekTarget;
    HI_S64 calcSeekMax = INT64_MAX;
    HI_S32 seekFlag = 0;

    if (flag == HI_DEMUXER_SEEK_BACKWARD) {
        calcSeekMin = INT64_MIN;
        calcSeekMax = seekTarget;
    }

    HI_S32 ret = avformat_seek_file(formatMember->formatContext, streamIndex, calcSeekMin,
        seekTarget, calcSeekMax, seekFlag);
    if (ret < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG,
            "error while seeking %lld ,streamIndex:%d, ret:%d\n", toMs, streamIndex, ret);
        return HI_ERR_DEMUXER_SEEK;
    }

    // pkt.pts is time base on stream->timebase, need expressed in AV_TIME_BASE units
    formatMember->lastReadPts = seekTarget;

    return HI_SUCCESS;
}

static HI_S32 DemuxerGetVideoInfo(const FfDemuxFormatMember *formatMember, HI_FORMAT_FILE_INFO_S *fmtInfo)
{
    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_WAV) {
        return HI_SUCCESS;
    }

    if (!(formatMember->videoStreamIndex != HI_DEMUXER_NO_MEDIA_STREAM && formatMember->videoStream)) {
        return HI_SUCCESS;
    }

    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_TS) {
        fmtInfo->s32UsedVideoStreamIndex = formatMember->usedVideoStreamIndex;
        fmtInfo->fFrameRate = TS_DEFAULT_FPS;
        fmtInfo->enVideoType = fmtInfo->stSteamResolution[formatMember->videoStreamIndex].enVideoType;
        for (HI_S32 index = 0; index < HI_DEMUXER_RESOLUTION_CNT; index++) {
            fmtInfo->stSteamResolution[index].u32Width = formatMember->stSteamResolution[index].u32Width;
            fmtInfo->stSteamResolution[index].u32Height = formatMember->stSteamResolution[index].u32Height;
            fmtInfo->stSteamResolution[index].s32VideoStreamIndex =
                formatMember->stSteamResolution[index].s32VideoStreamIndex;
            fmtInfo->stSteamResolution[index].enVideoType = formatMember->stSteamResolution[index].enVideoType;
        }
        fmtInfo->enVideoType = fmtInfo->stSteamResolution[formatMember->videoStreamIndex].enVideoType;
    } else if (formatMember->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        for (HI_S32 index = 0; index < HI_DEMUXER_RESOLUTION_CNT; index++) {
            fmtInfo->stSteamResolution[index].u32Width = formatMember->stSteamResolution[index].u32Width;
            fmtInfo->stSteamResolution[index].u32Height = formatMember->stSteamResolution[index].u32Height;
            fmtInfo->stSteamResolution[index].s32VideoStreamIndex =
                formatMember->stSteamResolution[index].s32VideoStreamIndex;

            fmtInfo->stSteamResolution[index].enVideoType = formatMember->stSteamResolution[index].enVideoType;
        }
        fmtInfo->s32UsedVideoStreamIndex = formatMember->videoStreamIndex;

        /* if video file duration short than 1s, den will be 0, use default fps 30 */
        AVRational frameRate = formatMember->videoStream->avg_frame_rate;

        fmtInfo->fFrameRate = (frameRate.den && frameRate.num) ?
            (HI_FLOAT)frameRate.num / (HI_FLOAT)frameRate.den : FFMPEG_DEFAULT_FPS;

        fmtInfo->enVideoType = fmtInfo->stSteamResolution[formatMember->videoStreamIndex].enVideoType;
    }

    if (fmtInfo->s32UsedVideoStreamIndex >= HI_DEMUXER_RESOLUTION_CNT) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "select videoidx: %d exceed max support %d \n",
            fmtInfo->s32UsedVideoStreamIndex, HI_DEMUXER_RESOLUTION_CNT - 1);
        return HI_ERR_DEMUXER_ACTION;
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerGetAudioInfo(const FfDemuxFormatMember *formatMember, HI_FORMAT_FILE_INFO_S *fmtInfo)
{
    if (!(formatMember->audioStreamIndex != HI_DEMUXER_NO_MEDIA_STREAM && formatMember->audioStream)) {
        return HI_SUCCESS;
    }

    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_TS) {
        fmtInfo->u32SampleRate = formatMember->sampleRate;
        fmtInfo->u32AudioChannelCnt = formatMember->chnnum;
    } else {
        fmtInfo->u32SampleRate = formatMember->audioStream->codecpar->sample_rate;
        fmtInfo->u32AudioChannelCnt = formatMember->audioStream->codecpar->channels;
    }

    if (formatMember->enFormat == HI_FFDEMUX_FORMAT_WAV) {
        fmtInfo->enAudioType = HI_FORMAT_AUDIO_TYPE_PCM;
    } else {
        if (formatMember->enAudCodecId == AV_CODEC_ID_PCM_ALAW) {
            fmtInfo->enAudioType = HI_FORMAT_AUDIO_TYPE_G711A;
        } else if (formatMember->enAudCodecId == AV_CODEC_ID_PCM_MULAW) {
            fmtInfo->enAudioType = HI_FORMAT_AUDIO_TYPE_G711U;
        } else {
            fmtInfo->enAudioType = HI_FORMAT_AUDIO_TYPE_AAC;
        }
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerGetMetaInfo(const FfDemuxFormatMember *formatMember, HI_FORMAT_FILE_INFO_S *fmtInfo)
{
    if (formatMember->enFormat != HI_FFDEMUX_FORMAT_MP4) {
        return HI_SUCCESS;
    }

    if (!formatMember->metaStream && !formatMember->videoStream) {
        fmtInfo->stSteamResolution[formatMember->videoStreamIndex].s32VideoStreamIndex =
            formatMember->videoStreamIndex;
        fmtInfo->fFrameRate = 0;
        fmtInfo->enVideoType = HI_FORMAT_VIDEO_TYPE_BUTT;
        fmtInfo->s32UsedVideoStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
    }

    if (!(formatMember->metaStreamIndex != HI_DEMUXER_NO_MEDIA_STREAM && formatMember->metaStream)) {
        return HI_SUCCESS;
    }

    fmtInfo->stSteamResolution[formatMember->metaStreamIndex].u32Width =
        formatMember->metaStream->codecpar->width;
    fmtInfo->stSteamResolution[formatMember->metaStreamIndex].u32Height =
        formatMember->metaStream->codecpar->height;
    fmtInfo->stSteamResolution[formatMember->metaStreamIndex].s32VideoStreamIndex =
        formatMember->metaStreamIndex;

    /* this demux lib will only deal with AV_CODEC_ID_H264/HI_FFMPEG_VIDEO_TYPE_H265 */
    if (formatMember->metaStream->codecpar->codec_id == AV_CODEC_ID_H264) {
        fmtInfo->stSteamResolution[formatMember->metaStreamIndex].enVideoType = HI_FORMAT_VIDEO_TYPE_H264;
    } else if (formatMember->metaStream->codecpar->codec_id == AV_CODEC_ID_HEVC) {
        fmtInfo->stSteamResolution[formatMember->metaStreamIndex].enVideoType = HI_FORMAT_VIDEO_TYPE_H265;
    } else {
        fmtInfo->stSteamResolution[formatMember->metaStreamIndex].enVideoType = HI_FORMAT_VIDEO_TYPE_BUTT;
    }

    return HI_SUCCESS;
}

static HI_S32 DemuxerGetFrameInfo(FfDemuxFormatMember *demuxer, HI_FORMAT_PACKET_S *fmtFrame, HI_BOOL *isVlidatePacket)
{
    HI_S32 ret;
    if (demuxer->pkt.stream_index == demuxer->audioStreamIndex) {
        if (demuxer->enAudCodecId == AV_CODEC_ID_PCM_MULAW ||
            demuxer->enAudCodecId == AV_CODEC_ID_PCM_ALAW ||
            demuxer->enFormat == HI_FFDEMUX_FORMAT_WAV) {
            ret = DemuxerSetAudioInfo(demuxer, fmtFrame);
        } else if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
            ret = Mp4SetAudioInfo(demuxer, fmtFrame);
        } else if (demuxer->enFormat == HI_FFDEMUX_FORMAT_TS) {
            ret = TsSetAudioInfo(demuxer, fmtFrame);
        } else {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "fileformat not support%d \n", demuxer->enFormat);
            return HI_FAILURE;
        }
    } else if (demuxer->pkt.stream_index == demuxer->usedVideoStreamIndex) {
        if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
            ret = Mp4SetVideoInfo(demuxer, fmtFrame);
        } else if (demuxer->enFormat == HI_FFDEMUX_FORMAT_TS) {
            ret = TsSetVideoInfo(demuxer, fmtFrame);
        } else {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "fileformat not support%d read video\n",
                           demuxer->enFormat);
            return HI_FAILURE;
        }
    } else {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
            "have other stream in input file, just ignore it\n");
        return HI_SUCCESS;
    }

    if (ret == HI_SUCCESS) {
        *isVlidatePacket = HI_TRUE;
    } else if (ret == HI_RET_FILE_EOF) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "SetFrameFino : file end\n");
    } else if (ret == HI_ERR_DEMUXER_SKIP_FILE) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "skip error frame\n");
        ret = HI_SUCCESS;
    } else if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "MP4_SET_Info(format = %d) exec failed \n",
                       demuxer->enFormat);
        ret = HI_ERR_DEMUXER_READ_PACKET;
    }

    return ret;
}

HI_S32 DemuxerRead(FfDemuxFormatMember *demuxer, HI_FORMAT_PACKET_S *fmtFrame)
{
    /* initialize packet, set data to HI_NULL, let the demuxer fill it */
    HI_BOOL isVlidatePacket = HI_FALSE;
    while (isVlidatePacket != HI_TRUE) {
        HI_S32 ret = DemuxerGetFrame(demuxer);
        if (ret != HI_SUCCESS) {
            return ret;
        }

        ret = DemuxerGetFrameInfo(demuxer, fmtFrame, &isVlidatePacket);
        if (isVlidatePacket != HI_TRUE) {
            av_packet_unref(&demuxer->pkt);
        }
        if (ret != HI_SUCCESS) {
            return ret;
        }
    }

    fmtFrame->s64Pts = demuxer->lastReadPts / FFMPEG_TIME_UNIT;
    fmtFrame->s64Dts = fmtFrame->s64Pts;
    fmtFrame->bKeyFrame = ((demuxer->pkt.flags & AV_PKT_FLAG_KEY) == AV_PKT_FLAG_KEY) ? HI_TRUE : HI_FALSE;
    fmtFrame->u32StreamIndex = demuxer->pkt.stream_index;
    fmtFrame->s64Duration = demuxer->pkt.duration;
    fmtFrame->s64Pos = demuxer->pkt.pos;
    if (demuxer->videoStream) {
        fmtFrame->pu8Header = demuxer->videoStream->codecpar->extradata;
        fmtFrame->u32HeaderLen = demuxer->videoStream->codecpar->extradata_size;
    }

    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG, "read index: %d pts: %lld len: %d\n",
        fmtFrame->u32StreamIndex, fmtFrame->s64Pts, fmtFrame->u32Size);

    return HI_SUCCESS;
}

static HI_VOID DemuxerCheckData(const FfDemuxFormatMember *demuxer, const HI_FORMAT_PACKET_S *fmtFrame)
{
    if (demuxer->pkt.data != fmtFrame->pu8Data) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "last read packet is not equal to the packet which is going to be freed \n");
    }

    return;
}

HI_S32 DemuxerFree(FfDemuxFormatMember *demuxer, const HI_FORMAT_PACKET_S *fmtFrame)
{
    if (demuxer->pkt.data == HI_NULL) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "you should call fmt_read first\n");
        return HI_ERR_DEMUXER_FREE_PACKET;
    }

    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        if (!(demuxer->hasAdts && demuxer->pkt.stream_index == demuxer->audioStreamIndex) == HI_FALSE) {
            DemuxerCheckData(demuxer, fmtFrame);
        }
    } else if (demuxer->enFormat == HI_FFDEMUX_FORMAT_WAV) {
        if (!(demuxer->pkt.stream_index == demuxer->audioStreamIndex)) {
            DemuxerCheckData(demuxer, fmtFrame);
        }
    } else {
        DemuxerCheckData(demuxer, fmtFrame);
    }

    av_packet_unref(&demuxer->pkt);
    return HI_SUCCESS;
}

HI_S32 DemuxerSeek(FfDemuxFormatMember *demuxer, HI_S32 streamIndex, HI_S64 toMs, HI_S32 flag)
{
    if (streamIndex == HI_DEMUXER_NO_MEDIA_STREAM) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "streamIndex  is  %d \n", streamIndex);
        return HI_ERR_DEMUXER_ILLEGAL_PARAM;
    }

    HI_S32 ret;
    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_TS) {
        ret = TsSeekMS(demuxer, toMs, flag);
        if (ret != HI_SUCCESS) {
            return ret;
        }

        ret = TsReadUntilIframe(demuxer);
        if (ret != HI_SUCCESS) {
            return HI_ERR_DEMUXER_SEEK;
        }
    } else {
        ret = DemuxerSeekfile(demuxer, streamIndex, toMs, flag);
        if (ret != HI_SUCCESS) {
            return ret;
        }
    }

    return HI_SUCCESS;
}

HI_S32 DemuxerProbe(FfDemuxFormatMember *demuxer)
{
    /* retrieve stream information */
    if (avformat_find_stream_info(demuxer->formatContext, HI_NULL) < 0) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "Could not find stream information \n");
        return HI_ERR_DEMUXER_PROBE;
    }

    HI_BOOL isBsfInited = HI_FALSE;
    // probe video when ts & mp4
    HI_S32 ret = DemuxerProbeVideo(demuxer, &isBsfInited);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerProbeVideo failed\n");
        return HI_ERR_DEMUXER_PROBE;
    }

    // probe audio
    ret = DemuxerProbeAudio(demuxer);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerProbeAudio failed\n");
        return HI_ERR_DEMUXER_PROBE;
    }

    // parse meta
    ret = DemuxerProbeMeta(demuxer);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerProbeMeta failed\n");
        return HI_ERR_DEMUXER_PROBE;
    }

    HI_S64 fileSize = 0;
    /* dump input information to stderr */
    if (DemuxerGetFileSize(demuxer, &fileSize) != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerGetFileSize exec failed \n");
        if (isBsfInited && demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
            Mp4AvbsfDeinit(&demuxer->h26XBsfCtx);
            demuxer->h26XBsfCtx = HI_NULL;
        }
        return HI_ERR_DEMUXER_PROBE;
    }

    demuxer->fileSize = fileSize;

    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO, "get out fmt_probe.\n");
    return HI_SUCCESS;
}

static HI_VOID GetFileInfoFromDemuxer(const FfDemuxFormatMember *demuxer, HI_FORMAT_FILE_INFO_S *fmtInfo)
{
    HI_S64 dudationMax = 0;
    if (demuxer->usedVideoStream && demuxer->audioStream) {
        HI_S64 videoDuration = 0;
        if (demuxer->usedVideoStream->time_base.den) {
            videoDuration = demuxer->usedVideoStream->duration * FFMPEG_TIME_UNIT *
                demuxer->usedVideoStream->time_base.num / demuxer->usedVideoStream->time_base.den;
        }
        HI_S64 audioDuration = 0;
        if (demuxer->audioStream->time_base.den) {
            audioDuration = demuxer->audioStream->duration * FFMPEG_TIME_UNIT *
                demuxer->audioStream->time_base.num / demuxer->audioStream->time_base.den;
        }
        dudationMax = (videoDuration >= audioDuration) ? videoDuration : audioDuration;
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_INFO,
            "get dudationMax %lld video %lld audio %lld\n", dudationMax, videoDuration, audioDuration);
    }

    fmtInfo->s64Duration = demuxer->formatContext->duration / FFMPEG_TIME_UNIT;
    fmtInfo->s64Duration = (fmtInfo->s64Duration >= dudationMax) ? fmtInfo->s64Duration : dudationMax;
    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        fmtInfo->s64StartTime = demuxer->formatContext->start_time / FFMPEG_TIME_UNIT;
    }
    fmtInfo->s64FileSize = demuxer->fileSize;

    fmtInfo->u32Bitrate = demuxer->formatContext->bit_rate;
}

HI_S32 DemuxerGetinfo(const FfDemuxFormatMember *formatMember, HI_FORMAT_FILE_INFO_S *fmtInfo)
{
    if ((formatMember->audioStreamIndex == HI_DEMUXER_NO_MEDIA_STREAM)
        && (formatMember->videoStreamIndex == HI_DEMUXER_NO_MEDIA_STREAM)) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "should call fmt_probe first \n");
        return HI_ERR_DEMUXER_ACTION;
    }

    if (memset_s(fmtInfo, sizeof(HI_FORMAT_FILE_INFO_S), 0x00, sizeof(HI_FORMAT_FILE_INFO_S)) != EOK) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "set  fmtInfo fail \n");
        return HI_FAILURE;
    }

    for (HI_S32 index = 0; index < HI_DEMUXER_RESOLUTION_CNT; index++) {
        fmtInfo->stSteamResolution[index].s32VideoStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
        fmtInfo->stSteamResolution[index].enVideoType = HI_FORMAT_VIDEO_TYPE_BUTT;
    }
    fmtInfo->enVideoType = HI_FORMAT_VIDEO_TYPE_BUTT;
    fmtInfo->s32UsedVideoStreamIndex = HI_DEMUXER_NO_MEDIA_STREAM;
    fmtInfo->s32UsedAudioStreamIndex = formatMember->audioStreamIndex;
    fmtInfo->enAudioType = HI_FORMAT_AUDIO_TYPE_BUTT;

    HI_S32 ret = DemuxerGetVideoInfo(formatMember, fmtInfo);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerGetVideoInfo fail \n");
        return ret;
    }

    // get audio info
    ret = DemuxerGetAudioInfo(formatMember, fmtInfo);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerGetAudioInfo fail \n");
        return ret;
    }

    // get audio info
    ret = DemuxerGetMetaInfo(formatMember, fmtInfo);
    if (ret != HI_SUCCESS) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR, "DemuxerGetMetaInfo fail \n");
        return ret;
    }
    GetFileInfoFromDemuxer(formatMember, fmtInfo);
    return HI_SUCCESS;
}

static HI_S32 DemuxerSetMp4Attr(FfDemuxFormatMember *demuxer, HI_FORMAT_MEDIA_ATTR_S *fmtInfo)
{
    if (fmtInfo->s32VidStreamId >= HI_DEMUXER_RESOLUTION_CNT) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "set MP4 video index:%d not support error \n", fmtInfo->s32VidStreamId);
        return HI_ERR_DEMUXER_NOT_SUPPORT;
    }

    HI_S32 index;
    HI_BOOL isFound = HI_FALSE;
    for (index = 0; index < HI_DEMUXER_RESOLUTION_CNT; index++) {
        if (fmtInfo->s32VidStreamId == demuxer->stSteamResolution[index].s32VideoStreamIndex) {
            isFound = HI_TRUE;
            break;
        }
    }

    if ((fmtInfo->s32VidStreamId != demuxer->videoStreamIndex) &&
        (fmtInfo->s32VidStreamId != demuxer->metaStreamIndex) &&
        (isFound == HI_FALSE)) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "set MP4 video index:%d not support error \n", fmtInfo->s32VidStreamId);
        return HI_ERR_DEMUXER_NOT_SUPPORT;
    }

    if (fmtInfo->s32VidStreamId == demuxer->videoStreamIndex) {
        demuxer->usedVideoStream = demuxer->videoStream;
        demuxer->enUsedVidCodecId = demuxer->vidCodecId;
        demuxer->playMetaVid = HI_FALSE;
    } else if (fmtInfo->s32VidStreamId == demuxer->metaStreamIndex) {
        demuxer->usedVideoStream = demuxer->metaStream;
        demuxer->enUsedVidCodecId = demuxer->enMetaCodecId;
        demuxer->playMetaVid = HI_TRUE;
    } else {
        demuxer->usedVideoStream = demuxer->formatContext->streams[index];
        demuxer->enUsedVidCodecId = demuxer->formatContext->streams[index]->codecpar->codec_id;
        demuxer->playMetaVid = HI_FALSE;

        const HI_CHAR *filterName = (AV_CODEC_ID_H264 == demuxer->enUsedVidCodecId) ? "h264_mp4toannexb" :
            "hevc_mp4toannexb";

        if (demuxer->h26XBsfCtx) {
            Mp4AvbsfDeinit(&demuxer->h26XBsfCtx);
            HI_S32 ret = Mp4AvbsfInit(&demuxer->h26XBsfCtx, demuxer->usedVideoStream, filterName);
            if (ret != HI_SUCCESS) {
                FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                    "Mp4AvbsfInit h264_mp4toannexb failed\n");
                return HI_ERR_DEMUXER_PROBE;
            }
        }
    }
    return HI_SUCCESS;
}

HI_S32 DemuxerSetattr(FfDemuxFormatMember *demuxer, HI_FORMAT_MEDIA_ATTR_S *fmtInfo)
{
    if (fmtInfo->s32AudStreamId != demuxer->audioStreamIndex) {
        FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
            "DemuxerSetattr audio index:%d not support error \n", fmtInfo->s32AudStreamId);
        return HI_ERR_DEMUXER_NOT_SUPPORT;
    }

    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_WAV) {  // when wav just set audio
        demuxer->audioStreamIndex = fmtInfo->s32AudStreamId;
        return HI_SUCCESS;
    }

    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_MP4) {
        HI_S32 ret = DemuxerSetMp4Attr(demuxer, fmtInfo);
        if (ret != HI_SUCCESS) {
            return ret;
        }
    }

    if (demuxer->enFormat == HI_FFDEMUX_FORMAT_TS) {
        if ((HI_U32)(fmtInfo->s32VidStreamId) >= demuxer->formatContext->nb_streams || fmtInfo->s32VidStreamId < 0) {
            FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_ERR,
                "set ts video index:%d not support error \n", fmtInfo->s32VidStreamId);
            return HI_ERR_DEMUXER_NOT_SUPPORT;
        }
        demuxer->usedVideoStream = demuxer->formatContext->streams[fmtInfo->s32VidStreamId];
        demuxer->enUsedVidCodecId = demuxer->vidCodecId;
    }

    demuxer->usedVideoStreamIndex = fmtInfo->s32VidStreamId;
    demuxer->audioStreamIndex = fmtInfo->s32AudStreamId;

    FFDEMUX_PRINTF(MODULE_NAME_DEMUX, HI_FFDEMUX_LOG_LEVEL_DEBUG, " stream index used now %d \n",
                   demuxer->usedVideoStreamIndex);
    return HI_SUCCESS;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
